if (!globalThis.print) {
    globalThis.print = console.log;
}

const TEST_ITERATIONS = 50000;
const DATA_PROP_KEY = "dataProp";
const ACCESSOR_PROP_KEY = "accessorProp";
const NON_EXISTENT_PROP_KEY = "nonExistentProp";

// --- Helper Functions ---

function createTargetObject() {
    return {
        [DATA_PROP_KEY]: "targetDataValue",
        _accessorValue: "targetAccessorValue",
        get [ACCESSOR_PROP_KEY]() {
            return this._accessorValue;
        },
        set [ACCESSOR_PROP_KEY](value) {
            this._accessorValue = value;
        },
    };
}

function createProtoObject() {
    return {
        protoDataProp: "protoDataValue",
        _protoAccessorValue: "protoAccessorValue",
        get protoAccessorProp() {
            return this._protoAccessorValue;
        },
        set protoAccessorProp(value) {
            this._protoAccessorValue = value;
        },
    };
}

function createInheritingObject(proto) {
    const obj = Object.create(proto);
    obj._accessorValue = "inheritingAccessorValue"; // For target's accessor
    obj._protoAccessorValue = "inheritingProtoAccessorValue"; // For proto's accessor
    return obj;
}

function createUnrelatedObject() {
    return {
        _accessorValue: "unrelatedAccessorValue",
        _protoAccessorValue: "unrelatedProtoAccessorValue",
        otherProp: "unrelatedOtherValue",
    };
}

function timeExecution(fn, iterations) {
    const startTime = Date.now();
    for (let i = 0; i < iterations; i++) {
        fn();
    }
    const endTime = Date.now();
    return endTime - startTime;
}

function formatTestResult(testName, time) {
    // Pad the test name for alignment
    const paddedName = testName.padEnd(55, " ");
    return `TEST_RESULT: ${paddedName} | TIME: ${time.toFixed(2)}`;
}

// --- Test Scenarios ---

function runDataPropertyTests(target, unrelatedReceiver, iterations) {
    print("--- Data Property Tests ---");

    const timeReceiverIsTarget = timeExecution(() => {
        Reflect.get(target, DATA_PROP_KEY, target);
    }, iterations);
    print(formatTestResult("DataProp_ReceiverIsTarget", timeReceiverIsTarget));

    const timeReceiverIsUnrelated = timeExecution(() => {
        Reflect.get(target, DATA_PROP_KEY, unrelatedReceiver);
    }, iterations);
    print(
        formatTestResult(
            "DataProp_ReceiverIsUnrelated",
            timeReceiverIsUnrelated
        )
    );

    const timeNonExistent = timeExecution(() => {
        Reflect.get(target, NON_EXISTENT_PROP_KEY, target);
    }, iterations);
    print(formatTestResult("DataProp_NonExistent", timeNonExistent));
    print("---------------------------");
}

function runAccessorPropertyTests(
    target,
    inheritingReceiver,
    unrelatedReceiver,
    iterations
) {
    print("--- Accessor Property Tests (Target Own) ---");

    const timeReceiverIsTarget = timeExecution(() => {
        Reflect.get(target, ACCESSOR_PROP_KEY, target); // this = target
    }, iterations);
    print(
        formatTestResult(
            "AccessorProp_ReceiverIsTarget (this=target)",
            timeReceiverIsTarget
        )
    );

    const timeReceiverIsInheriting = timeExecution(() => {
        Reflect.get(target, ACCESSOR_PROP_KEY, inheritingReceiver); // this = inheritingReceiver
    }, iterations);
    print(
        formatTestResult(
            "AccessorProp_ReceiverIsInheriting (this=inheriting)",
            timeReceiverIsInheriting
        )
    );

    const timeReceiverIsUnrelated = timeExecution(() => {
        Reflect.get(target, ACCESSOR_PROP_KEY, unrelatedReceiver); // this = unrelatedReceiver
    }, iterations);
    print(
        formatTestResult(
            "AccessorProp_ReceiverIsUnrelated (this=unrelated)",
            timeReceiverIsUnrelated
        )
    );
    print("----------------------------------------------");
}

function runInheritedDataPropertyTests(
    instance,
    unrelatedReceiver,
    iterations
) {
    print("--- Inherited Data Property Tests ---");
    const PROP_KEY = "protoDataProp";

    const timeReceiverIsInstance = timeExecution(() => {
        Reflect.get(instance, PROP_KEY, instance);
    }, iterations);
    print(
        formatTestResult(
            "InheritedData_ReceiverIsInstance",
            timeReceiverIsInstance
        )
    );

    const timeReceiverIsUnrelated = timeExecution(() => {
        Reflect.get(instance, PROP_KEY, unrelatedReceiver);
    }, iterations);
    print(
        formatTestResult(
            "InheritedData_ReceiverIsUnrelated",
            timeReceiverIsUnrelated
        )
    );
    print("-------------------------------------");
}

function runInheritedAccessorPropertyTests(
    instance,
    instanceSibling,
    unrelatedReceiver,
    iterations
) {
    print("--- Inherited Accessor Property Tests ---");
    const PROP_KEY = "protoAccessorProp";

    const timeReceiverIsInstance = timeExecution(() => {
        Reflect.get(instance, PROP_KEY, instance); // this = instance
    }, iterations);
    print(
        formatTestResult(
            "InheritedAccessor_ReceiverIsInstance (this=instance)",
            timeReceiverIsInstance
        )
    );

    const timeReceiverIsSibling = timeExecution(() => {
        Reflect.get(instance, PROP_KEY, instanceSibling); // this = instanceSibling
    }, iterations);
    print(
        formatTestResult(
            "InheritedAccessor_ReceiverIsSibling (this=sibling)",
            timeReceiverIsSibling
        )
    );

    const timeReceiverIsUnrelated = timeExecution(() => {
        Reflect.get(instance, PROP_KEY, unrelatedReceiver); // this = unrelatedReceiver
    }, iterations);
    print(
        formatTestResult(
            "InheritedAccessor_ReceiverIsUnrelated (this=unrelated)",
            timeReceiverIsUnrelated
        )
    );
    print("-----------------------------------------");
}

// --- Main Execution ---

print(
    `PERFORMANCE TEST: Reflect.get with Receiver (Iterations: ${TEST_ITERATIONS})`
);
print("======================================================================");

const target = createTargetObject();
const proto = createProtoObject();
const inheritingInstance = createInheritingObject(target); // Inherits from target (for accessor test)
const instanceWithProto = createInheritingObject(proto); // Inherits from proto (for inherited tests)
const instanceSibling = createInheritingObject(proto); // Another object inheriting from proto
const unrelatedReceiver = createUnrelatedObject();

// --- Run Tests ---
runDataPropertyTests(target, unrelatedReceiver, TEST_ITERATIONS);
print();
runAccessorPropertyTests(
    target,
    inheritingInstance,
    unrelatedReceiver,
    TEST_ITERATIONS
);
print();
runInheritedDataPropertyTests(
    instanceWithProto,
    unrelatedReceiver,
    TEST_ITERATIONS
);
print();
runInheritedAccessorPropertyTests(
    instanceWithProto,
    instanceSibling,
    unrelatedReceiver,
    TEST_ITERATIONS
);

print("======================================================================");
print("Test execution finished.");
